﻿using Skybot.Net.WebSockets.Sockets;
/*
 参见:https://github.com/wen866595/open-doc/blob/master/rfc/RFC6455-cn.md
 * RFC6455
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Skybot.Net.WebSockets
{
    public class WebSocketHost:System.IDisposable
    {

        /// <summary>
        /// 接入的客户端用户
        /// </summary>
        private System.Collections.Concurrent.ConcurrentDictionary<string, SocketClientConnection> _WebSocketClients = new System.Collections.Concurrent.ConcurrentDictionary<string, SocketClientConnection>();
        /// <summary>
        /// 接入的客户端用户
        /// </summary>
        public System.Collections.Concurrent.ConcurrentDictionary<string, SocketClientConnection> WebSocketClients
        {
            get { return _WebSocketClients; }

        }


        /// <summary>
        /// 接收到字符串数据
        /// </summary>
        public Action<SocketClientConnection, string> OnReceivedString { get; set; }

        /// <summary>
        /// WebSocket服务
        /// </summary>
        protected AsyncSocketServer server;

        /// <summary>
        /// 接收数据队列
        /// </summary>
        protected System.Collections.Concurrent.ConcurrentQueue<SocketClientConnection> ReceiveDataQueue = new System.Collections.Concurrent.ConcurrentQueue<SocketClientConnection>();

        /// <summary>
        /// 创建一个新的WebHost主机 构造函数
        /// </summary>
        public WebSocketHost()
        {
            Initalize();
        }

        /// <summary>
        /// 线程控制
        /// </summary>
        protected System.Threading.AutoResetEvent wait = new System.Threading.AutoResetEvent(false);

        /// <summary>
        /// 初始化
        /// </summary>
        protected   void Initalize()
        {
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {

                while (true)
                {
                    //当一次取完所有数据时才进行线程阻止
                    if (!CycleParse())
                    {
                        wait.WaitOne();
                    }
                }

            });
        }
         
        /// <summary>
        /// 循环获取数据
        /// </summary>
        protected virtual bool CycleParse()
        {

            SocketClientConnection conn;
            if (ReceiveDataQueue.TryDequeue(out conn))
            {
                bool canrun = true;
                while (canrun)
                {
                    ReadState connState = conn.ConnUserToKen as ReadState;
                    //检查联接是否正确初始化
                    if (connState == null)
                    {
                        return true;
                    }
                    byte[] frame = ParseFrame.GetFrameData(conn, connState);
                    if (frame != null)
                    {
                        var str = System.Text.UTF8Encoding.UTF8.GetString(frame);
                        OnReceivedString(conn, str);
                    }
                    else
                    {
                        if (connState.ReceiveData.Count < 6)
                        {
                            canrun = false;
                            break;
                        }
                    }
                }
                return true;
            }
            return false;
        }


        /// <summary>
        /// 需要创建一个监听的端口,一个实例只能调用一此此方法
        /// </summary>
        /// <param name="endPoint"></param>
        public void Start(System.Net.IPEndPoint endPoint)
        {
            if (server != null)
            {
                throw new NotSupportedException("一个实例不支持多次调用此方法.");
            }

            //初始化服务
            //使用此方法可以防止多次初始化
            System.Threading.LazyInitializer.EnsureInitialized(ref server, () =>
            {
                server = new AsyncSocketServer(endPoint);

                System.Threading.Tasks.Task.Factory.StartNew(server.Start);
                server.OnAcceptSocket += server_OnAcceptSocket;
                server.OnClientDisconnected += server_OnClientDisconnected;
                server.OnReceiveData += server_OnReceiveData;
                return server;
            });
        }

        #region 连接断开接入管理
        /// <summary>
        /// 断开联接时发生
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        void server_OnClientDisconnected(object sender, SocketAsyncEventArgs e)
        {
            if (e.UserToken is SocketClientConnection)
            {
                SocketClientConnection conn = e.UserToken as SocketClientConnection;
                WebSocketClients.TryRemove(conn.RemoteEndPoint, out conn);
                if (conn != null)
                {
                    Console.WriteLine(string.Format("节点:{0},连接断开", conn.RemoteEndPoint));
                }

            }
        }

        void server_OnAcceptSocket(object sender, SocketAsyncEventArgs e)
        {


            string key = GetEpKey(e.AcceptSocket.RemoteEndPoint);
            SocketClientConnection conn = new SocketClientConnection()
            {
                AsyncEventArgs = e,
                AcceptDateTime = DateTime.Now,
                RemoteEndPoint = key,
                Socket = e.AcceptSocket
            };

            e.UserToken = conn;
            WebSocketClients.TryAdd(key, conn);
            Console.WriteLine(string.Format("节点:{0},连接接入...", conn.RemoteEndPoint));
        }
        #endregion


        void server_OnReceiveData(object sender, SocketAsyncEventArgs e)
        {
            byte[] receiveBuffer = new byte[e.BytesTransferred];
            Buffer.BlockCopy(e.Buffer, 0, receiveBuffer, 0, receiveBuffer.Length);

            if (e.UserToken is SocketClientConnection)
            {
                SocketClientConnection conn = e.UserToken as SocketClientConnection;
                if (conn.ConnUserToKen == null || !(conn.ConnUserToKen is ReadState))
                {
                    #region 状态初始化握手
                    List<byte> httpHead = null;
                    #region 网络环境不好情况分包接收HTTP头数据
                    if (conn.ConnUserToKen == null)
                    {
                        conn.ConnUserToKen = httpHead = new List<byte>();
                    }
                    else
                    {
                        httpHead = conn.ConnUserToKen as List<byte>;
                    }

                    //如果数据大于200并且以\r\n\r\n结束 
                    string hex = string.Empty;
                    if (httpHead != null)
                    {

                        httpHead.AddRange(receiveBuffer);
                        hex = System.Text.ASCIIEncoding.ASCII.GetString(httpHead.ToArray());
                    }
                    //协议头过长检查,因为没有握手成功之前不可能发其它数据
                    if (hex.Length > 1024)
                    {
                        Console.WriteLine(string.Format("连接数据错误握手头过长,服务端主动断开,{0}", conn.RemoteEndPoint));
                        //关闭联接
                        conn.Socket.Close();
                    }
                    #endregion

                    if (hex.EndsWith("\r\n\r\n") && hex.Length > 80)
                    {

                        ReadState connState = new ReadState();
                        WebSocketHttpRequestHead head = new WebSocketHttpRequestHead(hex);
                        if (!string.IsNullOrEmpty(head.Sec_WebSocket_Key))
                        {
                            var ver = head.Sec_WebSocket_Key.Trim();
                            //发送握手
                            byte[] handBytes = ProtocolRFC6455.BuildHandshake(head, null);
                            conn.Socket.Send(handBytes);
                            conn.ConnUserToKen = connState;
                        }
                        else
                        {
                            Console.WriteLine(string.Format("连接数据错误,服务端主动断开,{0}", conn.RemoteEndPoint));
                            //关闭联接
                            conn.Socket.Close();
                        }
                        //清空数据
                        httpHead.Clear();
                    }

                    #endregion


                }
                else
                {
                    ReadState connState = conn.ConnUserToKen as ReadState;
                    //检查联接是否正确初始化
                    if (connState == null)
                    {

                        Console.WriteLine(string.Format("联接末初始化"));
                        //关闭联接
                        conn.Socket.Close();
                        return;
                    }
#if RFC6455调试代码
                    #region 调试代码
                    if (receiveBuffer.Length > 2)
                    {

                        byte bt1 = receiveBuffer[0];
                        byte bt2 = receiveBuffer[1];
                        int Opcode = ((bt1 >> 4) & 0xF);

                        Console.WriteLine(string.Format("FIN:{0},RSV1:{1} RSV2:{2} RSV3:{3};Opcode:{4},mask:{5};len:{6}"
                            , bt1 >> 7 & 1
                            , (bt1 >> 6) & 1
                            , (bt1 >> 5) & 1
                            , (bt1 >> 4) & 1
                            , (bt1 & 0xF).ToString("X2")
                            ///////第二字节
                            , bt2 >> 7 & 1 //mask
                            , bt2 & 0x7f //Payload len   ==((byte)(bt2<<1)>>1)
                            ));
                        //FIN：1位，用来表明这是一个消息的最后的消息片断，当然第一个消息片断也可能是最后的一个消息片断
                        //RSV1, RSV2, RSV3: 分别都是1位，如果双方之间没有约定自定义协议，那么这几位的值都必须为0,否则必须断掉WebSocket连接
                        //Opcode:4位操作码，定义有效负载数据，如果收到了一个未知的操作码，连接也必须断掉，以下是定义的操作码：
                        //*  %x0 表示连续消息片断
                        //*  %x1 表示文本消息片断
                        //*  %x2 表未二进制消息片断
                        //*  %x3-7 为将来的非控制消息片断保留的操作码
                        //*  %x8 表示连接关闭
                        //*  %x9 表示心跳检查的ping
                        //*  %xA 表示心跳检查的pong
                        //*  %xB-F 为将来的控制消息片断的保留操作码
                    }
                    #endregion
#endif
                    lock (connState.ReceiveData)
                    {
                        //添加接收到的数据到集合
                        connState.ReceiveData.AddRange(receiveBuffer);
                    }
                    if (false)
                    {
                        #region 调用原方式解析

                        ProtocolRFC6455.ReceiveData(connState.ReceiveData, connState, ReceiveData, conn);
                        #endregion

                    }
                    else
                    {


#if old
                        #region 多线程使用有访问冲突情况斩不使用
                                                //添加到队列中
                        ReceiveDataQueue.Enqueue(conn);
                        //可以开始循环
                        wait.Set();
                        #endregion
#endif
                        #region 解析客户端数据
                        try
                        {
                            bool canrun = true;
                            int max = 1;
                            while (canrun)
                            {
                                byte[] frame = ParseFrame.GetFrameData(conn, connState);
                                if (frame != null)
                                {
                                    var str = Encoding.UTF8.GetString(frame);
                                    OnReceivedString(conn, str);
                                }
                                else
                                {
                                    if (connState.ReceiveData.Count < 6 || max > 10)
                                    {
                                        canrun = false;
                                        break;
                                    }
                                }
                                max++;
                            }
                        }
                        catch
                        {
                            try
                            {
                                //数据错误直接断开联接
                                connState.ReceiveData.Clear();
                                conn.Socket.Close();
                            }
                            catch
                            {
                            }
                        }

                        #endregion

                    }
                }
            }



        }
        /// <summary>
        /// 解析贴
        /// </summary>
        protected ClientFrameParse ParseFrame = new ClientFrameParse();
        /// <summary>
        /// 获取当前联接节点的Key
        /// </summary>
        /// <returns></returns>
        protected string GetEpKey(System.Net.EndPoint conn)
        {

            return string.Format("{0}", conn.ToString());
        }

 
        /// <summary>
        /// 接收一帧数据
        /// </summary>
        /// <param name="frameType">数据类型</param>
        /// <param name="data">当前数据</param>
        /// <param name="conn">联接对像</param>
        protected void ReceiveData(SocketClientConnection conn, FrameType frameType, byte[] data)
        {
            switch (frameType)
            {
                case FrameType.Text:
                    if (data != null && data.Length > 0)
                    {
                        if (OnReceivedString != null)
                        {
                            OnReceivedString(conn, System.Text.UTF8Encoding.UTF8.GetString(data));
                        }
                    }
                    break;
            }
        }

        #region 释放资源

        public void Dispose()
        {
            Dispose(true);
        }
        protected virtual void Dispose(bool b)
        {
            //GC.SuppressFinalize
            wait.Dispose();
        }
        #endregion
    }
}
